import { _decorator, Animation, AudioSource, Camera, Color, Component, find, geometry, instantiate, Node, PhysicsSystem, Prefab, resources, Sprite, SpriteFrame, tween, UITransform, Vec3 } from 'cc';
import { CardClickManager } from './CardClickManager';
import { GLOBALUtil } from './GlobalUtil';
import { GLOBALVar } from './GlobalVar';
import { GLOBALPOP } from './GlobalPop';
const { ccclass, property } = _decorator;

enum PileType {
    ONE_PILE_ONE,
    ONE_PILE_TWO,
    ONE_PILE_FOUR
}
const CARD_WIDTH: number = 80;
const CARD_HEIGHT: number = 100;

@ccclass('CardGenerate')
class CardGenerate extends Component {

    private static _instance: CardGenerate = null;
    private CommonCardPrefabs: Prefab[] = [];
    private SpecialCardPrefabs: Prefab[] = [];
    private CardsInMap: Node[] = [];
    private CurrentCamera: Camera = null;
    private CardContainer: Node = null;

    private OffSets: { x: number, y: number }[] = [
        { x: -38, y: -48 },
        { x: 38, y: -48 },
        { x: -38, y: 48 },
        { x: 38, y: 48 },
    ];

    private Coordinate: [number, number] | null = null;
    private CoordinateMap: { [key: string]: [number, number][] } = {};

    public static getInstance(): CardGenerate {
        if (this._instance == null) {
            this._instance = new CardGenerate();
        }
        return this._instance;
    }

    public setCurrentCamera(camera: Camera) {
        this.CurrentCamera = camera;
    }

    public setCardContainer(node: Node) {
        this.CardContainer = node;
    }

    private async initCardPrefabs(TypeNums: number): Promise<void> {
        this.CommonCardPrefabs = [];
        this.SpecialCardPrefabs = [];

        if (TypeNums > 14) {
            console.error("所取卡牌种类数量大于卡牌总种类数量");
            return;
        }
        if (TypeNums <= 0) {
            console.error("卡牌种类数量不能小于1");
            return;
        }

        const RANDOMNUM: number[] = GLOBALUtil.getMultiRandomInt(TypeNums, 0, 14);

        for (const value of RANDOMNUM) {
            try {
                const prefab = await this.loadPrefab("Prefabs/CardPrefabs/Card_" + value);
                this.CommonCardPrefabs.push(prefab);
            } catch (err) {
                console.error(err);
                throw err;
            }
        }

        for (let i = 1; i <= 2; i++) {
            try {
                const prefab = await this.loadPrefab("Prefabs/CardPrefabs/SpecialCard_" + i);
                this.SpecialCardPrefabs.push(prefab);
            } catch (err) {
                console.error(err);
                throw err;
            }
        }
    }

    private loadPrefab(path: string): Promise<Prefab> {
        return new Promise((resolve, reject) => {
            resources.load(path, Prefab, (err, prefab) => {
                if (err) {
                    reject(err);
                } else {
                    resolve(prefab);
                }
            });
        });
    }

    private initCoordinates(CircleNum: number, CardContainer: Node) {
        if (CircleNum != 2 && CircleNum != 3) {
            console.error("CircleNum must be 2 or 3");
        } else {
            this.CoordinateMap["CoorFullMap"] = [];
            let ParentWorldPos = CardContainer.getWorldPosition();
            for (let i = -CircleNum * 2; i <= CircleNum * 2; i++) {
                for (let j = -CircleNum * 2; j <= CircleNum * 2; j++) {
                    let x = 0.5 * CARD_WIDTH * j + ParentWorldPos.x;
                    let y = 0.5 * CARD_HEIGHT * i + ParentWorldPos.y;
                    this.Coordinate = [x, y];
                    this.CoordinateMap["CoorFullMap"].push(this.Coordinate);
                }
            }
        }
    }

    private async applyCardFrameToInstance(cardNode: Node, cardFrameUsed: string) {
        const cardFramePath = "CardFrames/CardFrame_" + cardFrameUsed + "/spriteFrame";
        const cardFrame = await new Promise<SpriteFrame>((resolve, reject) => {
            resources.load(cardFramePath, SpriteFrame, (err, spriteFrame) => {
                if (err) {
                    console.error(err);
                    reject(err);
                } else {
                    resolve(spriteFrame);
                }
            });
        });

        const cardFrameNode = cardNode.getChildByName("CardFrame");
        if (cardFrameNode) {
            const sprite = cardFrameNode.getComponent(Sprite);
            if (sprite) {
                sprite.spriteFrame = cardFrame;
            }
        }
    }

    public async generateCommonCardFullMap(CardTypeNum: number, CardTotalNum: number, CircleNum: number, Parent: Node, GenSpecialCard: boolean = false, SpecialCardCount: number = 0, callback?: Function) {
        if (CardTotalNum % 3 != 0) {
            console.error("CardTotalNum must be a multiple of 3");
            return;
        }
        if (CardTypeNum * 3 > CardTotalNum) {
            console.error("卡牌总数不足");
            return;
        }

        try {
            await this.initCardPrefabs(CardTypeNum);
            this.initCoordinates(CircleNum, Parent);
            const CardFrameUsed = GLOBALVar.CardFrameUsed;

            let PerCardPrefabNum: Map<number, number> = new Map();
            let totalSpecialCards = 0;
            if (GenSpecialCard) {
                for (let i = 0; i < SpecialCardCount; i++) {
                    let specialIndex = i % this.SpecialCardPrefabs.length;
                    let currentCount = PerCardPrefabNum.get(this.CommonCardPrefabs.length + specialIndex);
                    PerCardPrefabNum.set(this.CommonCardPrefabs.length + specialIndex, (currentCount || 0) + 1);
                }
                totalSpecialCards = SpecialCardCount;
            }

            for (let i = 0; i < CardTotalNum - totalSpecialCards; i += 3) {
                let RandomInt = GLOBALUtil.getSingleRandomInt(this.CommonCardPrefabs.length);
                let CurrentNum = PerCardPrefabNum.get(RandomInt);
                CurrentNum ? PerCardPrefabNum.set(RandomInt, CurrentNum + 3) : PerCardPrefabNum.set(RandomInt, 3);
            }

            let zIDX = 0;
            while (PerCardPrefabNum.size > 0) {
                let PerCardPrefabNumNotZero: number[] = [];
                PerCardPrefabNum.forEach((value, key) => {
                    if (value === 0) {
                        PerCardPrefabNum.delete(key);
                    } else {
                        PerCardPrefabNumNotZero.push(key);
                    }
                });
                if (PerCardPrefabNum.size === 0) {
                    break;
                }
                const TEMP_NUM = GLOBALUtil.getSingleRandomInt(PerCardPrefabNumNotZero.length);
                let RandomInt = GLOBALUtil.getSingleRandomInt(this.CoordinateMap["CoorFullMap"].length);
                let prefabArray = this.CommonCardPrefabs;
                let isSpecialCard = false;
                if (PerCardPrefabNumNotZero[TEMP_NUM] >= this.CommonCardPrefabs.length) {
                    prefabArray = this.SpecialCardPrefabs;
                    isSpecialCard = true;
                    const specialIndex = PerCardPrefabNumNotZero[TEMP_NUM] - this.CommonCardPrefabs.length;
                }
                let Card: Node = instantiate(prefabArray[isSpecialCard ? PerCardPrefabNumNotZero[TEMP_NUM] - this.CommonCardPrefabs.length : PerCardPrefabNumNotZero[TEMP_NUM]]);
                let vec3 = new Vec3(this.CoordinateMap["CoorFullMap"][RandomInt][0], this.CoordinateMap["CoorFullMap"][RandomInt][1], zIDX);
                zIDX += 1;
                Card.parent = Parent;
                Card.setWorldPosition(vec3);
                Card.name = "Card_" + zIDX;
                this.CardsInMap.push(Card);
                PerCardPrefabNum.set(PerCardPrefabNumNotZero[TEMP_NUM], PerCardPrefabNum.get(PerCardPrefabNumNotZero[TEMP_NUM]) - 1);
                PerCardPrefabNumNotZero = [];

                if (!isSpecialCard && CardFrameUsed != null) {
                    await this.applyCardFrameToInstance(Card, CardFrameUsed);
                }
            }
            callback();
        } catch (error) {
            console.error("加载卡牌预制体失败！", error);
        }
    }

    public initCardCover(Camera: Camera) {
        this.CurrentCamera = Camera;
        let ray = new geometry.Ray();
        this.CardsInMap.forEach((CARD) => {
            this.OffSets.forEach((v) => {
                const WorldPos = CARD.getWorldPosition();
                WorldPos.x += v.x;
                WorldPos.y += v.y;
                const ScreenPos = Camera.worldToScreen(WorldPos);
                const CARD_NAME = CARD.name;
                Camera.screenPointToRay(ScreenPos.x, ScreenPos.y, ray);
                if (PhysicsSystem.instance.raycastClosest(ray)) {
                    const raycastClosestResult = PhysicsSystem.instance.raycastClosestResult;
                    const collider = raycastClosestResult.collider;
                    const _CARD = collider.node;
                    const name = _CARD.name;
                    if (name === CARD_NAME) {
                        const TS = _CARD.getComponent("CardClickManager") as CardClickManager;
                        const CARD_RAY_COUNT = TS.getCardrayCount();
                        TS.setCardrayCount(CARD_RAY_COUNT + 1);
                    }
                }
            });
        });
    }

    public updateCardCover(POS: Vec3) {
        let ray = new geometry.Ray();
        this.OffSets.forEach((v) => {
            const WorldPos = new Vec3(POS.x + v.x, POS.y + v.y, POS.z);
            const ScreenPos = this.CurrentCamera.worldToScreen(WorldPos);
            this.CurrentCamera.screenPointToRay(ScreenPos.x, ScreenPos.y, ray);
            if (PhysicsSystem.instance.raycastClosest(ray)) {
                const raycastClosestResult = PhysicsSystem.instance.raycastClosestResult;
                const collider = raycastClosestResult.collider;
                const _CARD = collider.node;
                const name = _CARD.name;
                const TS = _CARD.getComponent("CardClickManager") as CardClickManager;
                TS.setCardrayCount(TS.getCardrayCount() + 1);
            }
        });
    }

    public getCardsInMap() {
        return this.CardsInMap;
    }

    private initMembers() {
        this.CardsInMap.forEach((card) => {
            card.destroy();
        });
        this.Coordinate = null;
        this.CoordinateMap = {};
        this.CardsInMap = [];
        GLOBALVar.clearCardInSlot();
    }

    public generateLevel() {
        this.initMembers();
        let level = GLOBALVar.getClassicModeLevel();
        let CardTypeNum: number;
        let CardTotalNum: number;
        let CircleNum: number;
        let GenSpecialCard: boolean;
        let SpecialCardCount: number;
        switch (level) {
            case 1:
                CardTypeNum = 5;
                CardTotalNum = 15;
                CircleNum = 2;
                GenSpecialCard = false;
                SpecialCardCount = 0;
                break;
            case 2:
                CardTypeNum = 8;
                CardTotalNum = 39;
                CircleNum = 2;
                GenSpecialCard = false;
                SpecialCardCount = 0;
                break;
            case 3:
                CardTypeNum = 12;
                CardTotalNum = 60;
                CircleNum = 3;
                GenSpecialCard = false;
                SpecialCardCount = 0;
                break;
            case 4:
                CardTypeNum = 14;
                CardTotalNum = 60;
                CircleNum = 3;
                GenSpecialCard = true;
                SpecialCardCount = 6; // 可以根据需要调整特殊卡牌的数量
                break;
            case 5:
                this.gotoVictory();
                break;
        }
        if (level < 5) {
            this.generateCommonCardFullMap(CardTypeNum, CardTotalNum, CircleNum, this.CardContainer, GenSpecialCard, SpecialCardCount, () => {
                setTimeout(() => {
                    this.initCardCover(this.CurrentCamera);
                }, 400);
            });
        }
    }

    gotoVictory() {
        // 通关第四关后呼出胜利界面
        GLOBALVar.setClassicModeLevel(1);
        GLOBALPOP.setGameState(1);
        resources.load('/Prefabs/VictoryNode', Prefab, (err, prefab) => {
            if (err) {
                console.error(err);
                return;
            }
            const victoryNode = instantiate(prefab);
            victoryNode.parent = find("Canvas");
        });
    }

    public restart() {
        GLOBALVar.setClassicModeLevel(1);
        GLOBALPOP.setGameState(1);
        this.generateLevel();
    }

    public playYouPass() {
        this.CardContainer.getComponent(AudioSource).play();
    }
}

export const CardGenerator: CardGenerate = CardGenerate.getInstance();